#pragma once
#ifndef __G_HANDLE_POOL_H__
#define __G_HANDLE_POOL_H__

#include <time.h>
#include <math.h>

#include "GTypes.h"
#include "GError.h"
#include "GInstanceId.h"

namespace GUtil
{

	#define G_POOL_MAX_INDEX	16383 // 14 bit uint
	#define G_POOL_MAX_RECYCLES	4095 // 12 bit uint

	class GHandle
	{
	template <typename ObjectType, GUInt32 Capacity>
	friend class GHandlePool;

	private:
		GUInt32	Index : 14;
		GUInt32	Recycles : 12;

		GHandle(GUInt32 TheIndex, GUInt32 TheRecycles)
		{
			Index = TheIndex;
			Recycles = TheRecycles;
		}

	public:
		GHandle()
		{
			Index = 0;
			Recycles = 0;
		}

		~GHandle()
		{
			
		}

		GUInt32 GetIndex() const
		{
			return Index;
		}

	};

	template <typename ObjectType, GUInt32 Capacity = 100>
	class GHandlePool
	{
	private:
		struct GHandleData
		{
			GUInt32	NextIndex : 14;
			GUInt32	Recycles : 12;
			GUInt32	InUse : 1;
			GUInt32	EoList : 1;

			ObjectType Data;
		};

		GUInt32	Count;
		GUInt32	FirstIndex;

		GHandleData	Handles[Capacity+1];

	public:
		GHandlePool()
		{
			Clear();
		}
		
		~GHandlePool()
		{
			
		}

		void Clear()
		{
			Count = 0;
			FirstIndex = 0;

			srand((unsigned int)time(NULL));
		
			for (GSize I = 0; I < Capacity; ++I )
			{
				Handles[I].NextIndex = I + 1;
				Handles[I].InUse = false;
				Handles[I].EoList = false;
				Handles[I].Recycles = (rand() % G_POOL_MAX_RECYCLES) + 1;
			}

			Handles[Capacity].NextIndex = 0;
			Handles[Capacity].InUse = false;
			Handles[Capacity].EoList = true;
			Handles[Capacity].Recycles = 0;
		}

		GUInt32	GetCount() const
		{
			return Count;
		}

		GUInt32	GetNextIndex() const
		{
			return FirstIndex;
		}

		bool CheckValid(const GHandle& Handle) const
		{
			bool Used = Handles[Handle.Index].InUse;
			bool EoL = Handles[Handle.Index].EoList;
			bool RecycleMatch = Handles[Handle.Index].Recycles == Handle.Recycles;

			return RecycleMatch && Used && !EoL;
		}

		bool Recycle(const GHandle& Handle)
		{
			bool Valid = CheckValid(Handle);
			if (!Valid)
			{
				GError::Error("Handle is not valid.");
				return false;
			}

			Handles[Handle.Index].NextIndex = FirstIndex;
			Handles[Handle.Index].InUse = false;
			Handles[Handle.Index].Recycles++;

			FirstIndex = Handle.Index;
			Count--;

			return true;
		}

		bool Replace(const GHandle& Handle, const ObjectType& Data)
		{
			bool Valid = CheckValid(Handle);
			if (!Valid)
			{
				GError::Error("Handle is not valid.");
				return false;
			}

			Handles[Handle.Index].Data = Data;

			return true;
		}
		
		bool Reserve(GHandle& OutHandle, const ObjectType& Data)
		{
			bool Valid = CheckValid(OutHandle);
			if (Valid)
			{
				GError::Error("Handle is still valid.");
				return false;
			}

			if (Handles[FirstIndex].EoList)
			{
				GError::Error("Max handle count reached for this pool.");
				return false;
			}

			GUInt32 CurrentIndex = FirstIndex;

			FirstIndex = Handles[CurrentIndex].NextIndex;

			Handles[CurrentIndex].InUse = true;
			Handles[CurrentIndex].NextIndex = 0;
			Handles[CurrentIndex].Data = Data;

			OutHandle.Recycles = Handles[CurrentIndex].Recycles;
			OutHandle.Index = CurrentIndex;

			Count++;
			return true;
		}

		const ObjectType* GetPtr(const GHandle& Handle) const
		{
			bool Valid = CheckValid(Handle);
			if (!Valid)
			{
				GError::Error("Handle is not valid.");
				return NULL;
			}

			return &Handles[Handle.Index].Data;
		}

		bool GetData(const GHandle& Handle, ObjectType& OutData)
		{
			bool Valid = CheckValid(Handle);
			if (!Valid)
			{
				GError::Error("Handle is not valid.");
				return false;
			}

			OutData = Handles[Handle.Index].Data;
			return true;
		}

	};

}

#endif